﻿using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;

namespace Universe.IoC
{
    public static class FormLifetime
    {
        public delegate void Action();
        
        public static void OnDisposingForm(this Control control, Action action)
        {
            IComponent component = new DisposingCode(action);
            AddComponentToControlOwner(control, component);
        }

        public static void RegisterDisposable(this Control control, IDisposable disposable)
        {
            IComponent component = new DisposableComponent(disposable);
            AddComponentToControlOwner(control, component);
        }

        public static void AddToContainer(this IDisposable disposable, IContainer container)
        {
            container.Add(new DisposableComponent(disposable));
        }

        private static void AddComponentToControlOwner(Control control, IComponent component)
        {
            Action work =
                delegate
                {
                    IContainer container = FindContainer(control);

                    if (container != null)
                    {

                        container.Add(component);
                    }
                };

            if (control.IsHandleCreated)
                work();
            else
                control.HandleCreated += delegate { work(); };

            control.ParentChanged += delegate { work(); };
        }

        private static IContainer FindContainer(Control control)
        {
            IContainer container = null;
            Control current = control;
            while(current != null)
            {
                FieldInfo fi = current.GetType().GetField(
                    "components",
                    BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);

                if (fi != null)
                {
                    container = fi.GetValue(current) as IContainer;
                    if (container != null)
                        break;
                }

                current = current.Parent;
            }
            return container;
        }

        class DisposingCode : IComponent
        {
            public event EventHandler Disposed;

            private Action action;
            private ISite site;

            public DisposingCode(Action action)
            {
                this.action = action;
            }

            public void Dispose()
            {
                Action copy = action;
                if (copy != null)
                {
                    action = null;
                    copy();
                    if (Disposed != null)
                        Disposed(copy, EventArgs.Empty);
                }
            }

            public ISite Site
            {
                get { return site; }
                set { site = value; }
            }
        }

        class DisposableComponent : IComponent
        {
            public event EventHandler Disposed;

            private IDisposable target;
            private ISite site;

            public DisposableComponent(IDisposable target)
            {
                this.target = target;
            }

            public void Dispose()
            {

                IDisposable copy = target;
                if (copy != null)
                {
                    target = null;
                    copy.Dispose();
                    if (Disposed != null)
                        Disposed(copy, EventArgs.Empty);
                }
            }

            public ISite Site
            {
                get { return site; }
                set { site = value; }
            }
        }
    }
}
